﻿using System.Windows.Forms;
using SlimDX;
using SlimDX.D3DCompiler;
using SlimDX.DXGI;
using SlimDX.Direct3D11;
using SlimDX.DirectInput;
using SlimDX.Windows;
using Buffer = System.Buffer;
using Device = SlimDX.Direct3D11.Device;
using Effect = SlimDX.Direct3D11.Effect;

namespace InsTron
{
    public enum GameMenuState : int
    {
        NewGame,
        Options,
        Exit
    }

    
    public class MenuView : IRenderable
    {
        private Device device;
        private DeviceContext immediateContext;
        private Effect effect;
        private EffectTechnique menuTechnique;
        private InputLayout menuInputLayout;
        private SlimDX.Direct3D11.Buffer simpleMenuBuffer;
        private Texture2D newGameTexture;
        private Texture2D optionsTexture;
        private Texture2D exitTexture;
        private static GameMenuState actualGameState;
        private RenderForm renderForm;


        public MenuView(Device device, RenderForm renderForm)
        {
            this.device = device;
            immediateContext = device.ImmediateContext;

            // set actual gameState
            actualGameState = GameMenuState.NewGame;

            this.renderForm = renderForm;
            renderForm.KeyDown -= KeyDownForRenderForm;
            renderForm.KeyDown += KeyDownForRenderForm;

            InitializeAll();
        }

        public void InitializeAll()
        {
            LoadEffects();
            ConfigureInputLayouts();
            LoadModels();
            LoadTextures();
        }


        public void DisposeAll()
        {
            renderForm.KeyDown -= KeyDownForRenderForm;
            newGameTexture.Dispose();
            optionsTexture.Dispose();
            exitTexture.Dispose();
            effect.Dispose();
            simpleMenuBuffer.Dispose();
            menuInputLayout.Dispose();
        }

        private void LoadEffects()
        {
            //wczytanie efektow z plikow i pobranie niezbednych technik
            using (var bytecode = ShaderBytecode.CompileFromFile("shaders\\MainMenu.fx", "fx_5_0"))
            {
                effect = new Effect(device, bytecode);
            }
            menuTechnique = effect.GetTechniqueByName("GameMenu");
        }


        private void ConfigureInputLayouts()
        {
            var inputSignature = menuTechnique.GetPassByIndex(0).Description.Signature;
            menuInputLayout = new InputLayout(device, inputSignature, VertexDeclarations.InputElements_PosTex);
            inputSignature.Dispose();            
        }


        private void LoadModels()
        {
            //wczytanie modeli z plikow
            var vertices = new DataStream(20 * 6, true, true);
            vertices.Write(new Vector3(1.0f, 1.0f, 1.0f));
            vertices.Write(new Vector2(1.0f, 0.0f));
            vertices.Write(new Vector3(1.0f, -1.0f, 1.0f));
            vertices.Write(new Vector2(1.0f, 1.0f));
            vertices.Write(new Vector3(-1.0f, -1.0f, 1.0f));
            vertices.Write(new Vector2(0.0f, 1.0f));
            vertices.Write(new Vector3(-1.0f, 1.0f, 1.0f));
            vertices.Write(new Vector2(0.0f, 0.0f));
            vertices.Write(new Vector3(1.0f, 1.0f, 1.0f));
            vertices.Write(new Vector2(1.0f, 0.0f));
            vertices.Write(new Vector3(-1.0f, -1.0f, 1.0f));
            vertices.Write(new Vector2(0.0f, 1.0f));
            vertices.Position = 0;

            simpleMenuBuffer = new SlimDX.Direct3D11.Buffer(device, vertices, 20 * 6, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
        }


        private void LoadTextures()
        {
            // wczytanie tekstur z plikow
            newGameTexture = Texture2D.FromFile(device, "assets\\textures\\menu_ng.jpg");
            optionsTexture = Texture2D.FromFile(device, "assets\\textures\\menu_o.jpg");
            exitTexture = Texture2D.FromFile(device, "assets\\textures\\menu_e.jpg");
        }


        private void ConfigureShaders()
        {
            // ustawianie zmiennych w shaderze
            switch (actualGameState)
            {
                case GameMenuState.NewGame:
                    using (var resource = new ShaderResourceView(device, newGameTexture))
                        effect.GetVariableByName("xMenuTexture").AsResource().SetResource(resource);
                    break;
                    
                case GameMenuState.Options:
                    using (var resource = new ShaderResourceView(device, optionsTexture))
                        effect.GetVariableByName("xMenuTexture").AsResource().SetResource(resource);
                    break;

                default:
                    using (var resource = new ShaderResourceView(device, exitTexture))
                        effect.GetVariableByName("xMenuTexture").AsResource().SetResource(resource);
                    break;
            }
        }

        public static void KeyDownForRenderForm(object sender, KeyEventArgs e)
        {
            var actualState = (int)actualGameState;
            switch (e.KeyCode)
            {
                case Keys.Up:
                    actualGameState = actualState > 0 ? (GameMenuState)(--actualState) : GameMenuState.Exit;
                    break;

                case Keys.Down:
                    actualGameState = actualState == (int)GameMenuState.Exit ? GameMenuState.NewGame : (GameMenuState)(++actualState);
                    break;

                case Keys.Enter:
                    InvokeGameStateChanging();
                    break;
            }
        }

        private static void InvokeGameStateChanging()
        {
            switch (actualGameState)
            {
                case GameMenuState.NewGame:
                    Program.ChangeGameState(EProgramState.Game);
                    break;

                case GameMenuState.Options:
                    Program.ChangeGameState(EProgramState.Lobby);
                    break;

                default:
                    Application.Exit();
                    break;
            }
        }

        public void RenderScene()
        {
            ConfigureShaders();
            immediateContext.InputAssembler.InputLayout = menuInputLayout;
            immediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            immediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(simpleMenuBuffer, 20, 0));
            for (int i = 0; i < menuTechnique.Description.PassCount; i++)
            {
                menuTechnique.GetPassByIndex(i).Apply(immediateContext);
                immediateContext.Draw(6, 0);
            }
        }
    }
}
